package com.dycong.common.database.hibernate3;

import com.dycong.common.common.StringUtil;
import com.dycong.common.common.page.Pagination;
import com.dycong.common.database.MyBeanUtils;
import org.hibernate.*;
import org.hibernate.criterion.*;
import org.hibernate.internal.CriteriaImpl;
import org.hibernate.internal.SessionImpl;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.transform.ResultTransformer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

/**
 * This class should preserve.
 *
 * @preserve
 */
public abstract class HibernateBaseDao<T, ID extends Serializable> implements IHibernateBaseDao<T, ID> {
    /**
     * 日志，可用于子类
     */
    protected Logger log = LoggerFactory.getLogger(getClass());

    /**
     * @param id
     * @return 持久化对象
     */

    public T get(ID id) {
        return get(id, false);
    }

    /**
     * @param id   对象ID
     * @param lock 是否锁定，使用LockMode.UPGRADE
     * @return 持久化对象
     */
    @SuppressWarnings("unchecked")
    public T get(ID id, boolean lock) {
        T entity;
        if (lock) {
            entity = (T) getSession().get(getEntityClass(), id,
                    LockMode.PESSIMISTIC_WRITE);
        } else {
            entity = (T) getSession().get(getEntityClass(), id);
        }
        return entity;
    }


    @Override
    public T save(T bean) {
        getSession().save(bean);
        return bean;
    }

    public T deleteById(ID id) {
        T entity = get(id);
        if (entity != null) {
            getSession().delete(entity);
        }
        return entity;
    }

    /**
     * 按属性查找对象列表
     */
    @SuppressWarnings("unchecked")
    protected List<T> findByProperty(String property, Object value) {
        Assert.hasText(property);
        return createCriteria(Restrictions.eq(property, value)).list();
    }

    /**
     * 按属性查找唯一对象
     */
    @SuppressWarnings("unchecked")
    protected T findUniqueByProperty(String property, Object value) {
        Assert.hasText(property);
        Assert.notNull(value);
        return (T) createCriteria(Restrictions.eq(property, value))
                .uniqueResult();
    }

    /**
     * 按属性统计记录数
     *
     * @param property
     * @param value
     * @return
     */
    protected int countByProperty(String property, Object value) {
        Assert.hasText(property);
        Assert.notNull(value);
        return ((Number) (createCriteria(Restrictions.eq(property, value))
                .setProjection(Projections.rowCount()).uniqueResult()))
                .intValue();
    }

    /**
     * 按Criterion查询列表数据.
     *
     * @param criterion 数量可变的Criterion.
     */
    @SuppressWarnings("unchecked")
    protected List findByCriteria(Criterion... criterion) {
        return createCriteria(criterion).list();
    }

    /**
     * 通过Updater更新对象
     *
     * @param updater
     * @return
     */
    @SuppressWarnings("unchecked")
    public T updateByUpdater(Updater<T> updater) {
        ClassMetadata cm = sessionFactory.getClassMetadata(getEntityClass());
        T bean = updater.getBean();
        T po = (T) getSession().get(getEntityClass(),
                cm.getIdentifier(bean, (SessionImpl) sessionFactory.getCurrentSession()));
        updaterCopyToPersistentObject(updater, po, cm);
        return po;
    }

    /**
     * 将更新对象拷贝至实体对象，并处理many-to-one的更新。
     *
     * @param updater
     * @param po
     */
    private void updaterCopyToPersistentObject(Updater<T> updater, T po,
                                               ClassMetadata cm) {
        String[] propNames = cm.getPropertyNames();
        String identifierName = cm.getIdentifierPropertyName();
        Object bean = updater.getBean();
        Object value;
        for (String propName : propNames) {
            if (propName.equals(identifierName)) {
                continue;
            }
            try {
                value = MyBeanUtils.getSimpleProperty(bean, propName);
                if (!updater.isUpdate(propName, value)) {
                    continue;
                }
                cm.setPropertyValue(po, propName, value);
            } catch (Exception e) {
                throw new RuntimeException(
                        "copy property to persistent object failed: '"
                                + propName + "'", e);
            }
        }
    }

    /**
     * 根据Criterion条件创建Criteria,后续可进行更多处理,辅助函数.
     */
    protected Criteria createCriteria(Criterion... criterions) {
        Criteria criteria = getSession().createCriteria(getEntityClass());
        for (Criterion c : criterions) {
            criteria.add(c);
        }
        return criteria;
    }

    /**
     * 获得Dao对于的实体类
     *
     * @return
     */
    abstract protected Class<T> getEntityClass();


    /**
     * HIBERNATE 的 order 属性
     */
    protected static final String ORDER_ENTRIES = "orderEntries";

    /**
     * 通过HQL查询对象列表
     *
     * @param hql    hql语句
     * @param values 数量可变的参数
     */
    @SuppressWarnings("unchecked")
    protected List find(String hql, Object... values) {
        return createQuery(hql, values).list();
    }

    /**
     * 通过HQL查询唯一对象
     */
    protected Object findUnique(String hql, Object... values) {
        return createQuery(hql, values).uniqueResult();
    }

    /**
     * 通过Finder获得分页数据
     *
     * @param finder   Finder对象
     * @param pageNo   页码
     * @param pageSize 每页条数
     * @return
     */
    @SuppressWarnings("unchecked")
    protected Pagination find(Finder finder, int pageNo, int pageSize) {
        int totalCount = countQueryResult(finder);
        Pagination p = new Pagination(pageNo, pageSize, totalCount);
        if (totalCount < 1) {
            p.setList(new ArrayList());
            return p;
        }
        Query query = getSession().createQuery(finder.getOrigHql());
        finder.setParamsToQuery(query);
        query.setFirstResult(p.getFirstResult());
        query.setMaxResults(p.getPageSize());
        if (finder.isCacheable()) {
            query.setCacheable(true);
        }
        List list = query.list();
        p.setList(list);
        return p;
    }

    /**
     * 通过SqlFinder获取分页数据
     * 不计总行数
     *
     * @param sqlFinder
     * @param pageNo
     * @param pageSize
     * @return
     */
    protected Pagination findBySqlWithNoCount(SqlFinder sqlFinder, int pageNo, int pageSize) {
        Pagination p = new Pagination(pageNo, pageSize, 0);
        Session session = null;
        try {
            session = openSession();
            Query query = session.createSQLQuery(sqlFinder.getOrigSql());
            sqlFinder.setParamsToQuery(query);
            query.setFirstResult(p.getFirstResult());
            query.setMaxResults(p.getPageSize());
            if (sqlFinder.isCacheable()) {
                query.setCacheable(true);
            }
            List list = query.list();
            p.setList(list);
        } catch (Exception e) {
            log.error("",e);
        } finally {
            closeSession(session);
        }
        return p;
    }

    /**
     * 通过SqlFinder获取分页数据
     *
     * @param sqlFinder
     * @param pageNo
     * @param pageSize
     * @return
     */
    protected Pagination findBySql(SqlFinder sqlFinder, int pageNo, int pageSize) {
        int totalCount = countQueryResultBySql(sqlFinder);
        Pagination p = new Pagination(pageNo, pageSize, totalCount);
        if (totalCount < 1) {
            p.setList(new ArrayList());
            return p;
        }
        Session session=openSession();
        Query query = session.createSQLQuery(sqlFinder.getOrigSql());
        sqlFinder.setParamsToQuery(query);
        query.setFirstResult(p.getFirstResult());
        query.setMaxResults(p.getPageSize());
        if (sqlFinder.getEntityClass()!=null){
            ((SQLQuery)query).addEntity(sqlFinder.getEntityClass());
        }
        if (sqlFinder.isCacheable()) {
            query.setCacheable(true);
        }
        List list = query.list();
        closeSession(session);
        p.setList(list);
        return p;
    }

    /**
     * 获取分页数据，单独语句查询出总数量
     * 使用mysql find_rows() 函数特性 来做分页
     * select SQL_CALC_FOUND_ROWS 列 from table
     * @param sqlFinder
     * @param pageNo
     * @param pageSize
     * @return
     */
    protected Pagination findrowsSql(SqlFinder sqlFinder, int pageNo, int pageSize) {
        return findBySpecialSql(sqlFinder,"SELECT FOUND_ROWS();", pageNo, pageSize);
    }

    /**
     * 获取分页数据，单独语句查询出总数量
     * @param sqlFinder
     * @param countSql 可为空
     * @param pageNo
     * @param pageSize
     * @return
     */
    protected Pagination findBySpecialSql(SqlFinder sqlFinder,String countSql, int pageNo, int pageSize) {

        Pagination p = new Pagination(pageNo, pageSize, 0);
        Session session = null;
        try {
            session = openSession();
            Query query = session.createSQLQuery(sqlFinder.getOrigSql());
            sqlFinder.setParamsToQuery(query);
            query.setFirstResult(p.getFirstResult());
            query.setMaxResults(p.getPageSize());
            if (sqlFinder.getEntityClass()!=null){
                ((SQLQuery)query).addEntity(sqlFinder.getEntityClass());
                //query.setCacheable(true);
            }
            List list = query.list();
            p.setList(list);

            if (StringUtil.isNotEmpty(countSql)){
                int totalCount = countQueryResultBySpecialSql(session,countSql);
                p.setTotalCount(totalCount);
                if (totalCount < 1) {
                    p.setList(new ArrayList());
                }
            }
        } catch (Exception e) {
            log.error("",e);
        } finally {
            closeSession(session);
        }
        return p;
    }

    /**
     * 通过Finder获得列表数据
     *
     * @param finder
     * @return
     */
    @SuppressWarnings("unchecked")
    protected List find(Finder finder) {
        Query query = getSession().createQuery(finder.getOrigHql());
        finder.setParamsToQuery(query);
        query.setFirstResult(finder.getFirstResult());
        if (finder.getMaxResults() > 0)
            query.setMaxResults(finder.getMaxResults());
        if (finder.isCacheable())
            query.setCacheable(true);
        List list = query.list();
        return list;
    }

    /**
     * 根据查询函数与参数列表创建Query对象,后续可进行更多处理,辅助函数.
     */
    protected Query createQuery(String queryString, Object... values) {
        Assert.hasText(queryString);
        Query queryObject = getSession().createQuery(queryString).setCacheable(false);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                queryObject.setParameter(i, values[i]);
            }
        }
        return queryObject;
    }

    /**
     * 通过Criteria获得分页数据
     *
     * @param crit
     * @param pageNo
     * @param pageSize
     * @return
     */
    @SuppressWarnings("unchecked")
    protected Pagination findByCriteria(Criteria crit, int pageNo, int pageSize) {
        CriteriaImpl impl = (CriteriaImpl) crit;
        // 先把Projection、ResultTransformer、OrderBy取出来,清空三者后再执行Count操作
        Projection projection = impl.getProjection();
        ResultTransformer transformer = impl.getResultTransformer();
        List<CriteriaImpl.OrderEntry> orderEntries;
        try {
            orderEntries = (List) MyBeanUtils
                    .getFieldValue(impl, ORDER_ENTRIES);
            MyBeanUtils.setFieldValue(impl, ORDER_ENTRIES, new ArrayList());
        } catch (Exception e) {
            throw new RuntimeException(
                    "cannot read/write 'orderEntries' from CriteriaImpl", e);
        }

        int totalCount = ((Number) crit.setProjection(Projections.rowCount())
                .uniqueResult()).intValue();
        Pagination p = new Pagination(pageNo, pageSize, totalCount);
        if (totalCount < 1) {
            p.setList(new ArrayList());
            return p;
        }

        // 将之前的Projection,ResultTransformer和OrderBy条件重新设回去
        crit.setProjection(projection);
        if (projection == null) {
            crit.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
        }
        if (transformer != null) {
            crit.setResultTransformer(transformer);
        }
        try {
            MyBeanUtils.setFieldValue(impl, ORDER_ENTRIES, orderEntries);
        } catch (Exception e) {
            throw new RuntimeException(
                    "set 'orderEntries' to CriteriaImpl faild", e);
        }
        crit.setFirstResult(p.getFirstResult());
        crit.setMaxResults(p.getPageSize());
        p.setList(crit.list());
        return p;
    }

    /**
     * 获得Finder的记录总数
     *
     * @param finder
     * @return
     */
    protected int countQueryResult(Finder finder) {
        Session session=openSession();
        Query query = session.createQuery(finder.getRowCountHql());
        finder.setParamsToQuery(query);
        if (finder.isCacheable()) {
            query.setCacheable(true);
        }
        int tmp= ((Number) query.iterate().next()).intValue();
        closeSession(session);
        return tmp;
    }

    /**
     * 获取SqlFinder的记录数
     *
     * @param sqlFinder
     * @return
     */
    protected int countQueryResultBySql(SqlFinder sqlFinder) {
        Session session=openSession();
        Query query = session.createSQLQuery(sqlFinder.getRowCountSql());
        sqlFinder.setParamsToQuery(query);
        if (sqlFinder.isCacheable()) {
            query.setCacheable(true);
        }
        List<BigInteger> list = query.list();
        closeSession(session);
        return list.get(0).intValue();
    }

    /**
     *自定义语句获取记录数
     *
     * @return
     */
    protected int countQueryResultBySpecialSql(Session session, String sql) {
        Session session1;
        if (session!=null){
            session1=session;
        }else{
            session1=openSession();
        }
        Number number = null;
        try {
            Query query = session1.createSQLQuery(sql);
            number = (Number)query.uniqueResult();
            return number.intValue();
        }catch (Exception e){
            log.error("",e);
        }finally {
            closeSession(session1);
        }
        return 0;
    }

    protected SessionFactory sessionFactory;

    @Autowired
    public void setSessionFactory(SessionFactory sessionfactory) {
        this.sessionFactory = sessionfactory;
    }

    protected Session getSession() {
        return sessionFactory.getCurrentSession();
    }

    /**
     * 通过openSession 获取的session必须手工关闭
     * @return
     */
    protected Session openSession() {
        return sessionFactory.openSession();
    }

    protected void closeSession(Session session) {
        try {
            if(null !=session) {
                session.close();
            }
        } catch (HibernateException e) {}
    }
}
